#ifndef _AMERICO_GROWING_ARRAY_H_
#define _AMERICO_GROWING_ARRAY_H_

#include "DL_Debug.h"
//#include "defaults.h"
#include <string.h>

namespace Utilities
{
	template <class Type,class CountType =unsigned int>
	class GrowingArray
	{
	private:
		CountType mySize;
		CountType myRealSize;
		Type *myContainer;
		CountType myGrowthFactor;
		bool myUseSafeModeFlag;

	public:
		GrowingArray()
		{
			mySize = 0;
			myContainer = 0;
			myRealSize = 0;
			myGrowthFactor = 0;
			myUseSafeModeFlag = true;
		}
		GrowingArray(int aNrOfRecomendedItems,int aItemIncreaseSize, bool aUseSafeModeFlag=true)
		{
			myContainer = 0;
			Init(aNrOfRecomendedItems, aItemIncreaseSize, aUseSafeModeFlag);
		}
		GrowingArray(const GrowingArray& aGrowingArray)
		{
			myContainer = 0;
			operator=(aGrowingArray);
		}
		~GrowingArray()
		{
			delete [] myContainer;
		}

		GrowingArray& operator=(const GrowingArray& aGrowingArray)
		{
			if( myContainer != NULL )
			{
				delete [] myContainer;
			}
			mySize = aGrowingArray.mySize;
			myRealSize = aGrowingArray.myRealSize;
			myGrowthFactor = aGrowingArray.myGrowthFactor;
			myUseSafeModeFlag = aGrowingArray.myUseSafeModeFlag;
			myContainer = 0;
			if (myGrowthFactor>0)
			{
				myContainer = new Type[myRealSize];
				if(myRealSize >0)
				{
					Copy(aGrowingArray.myContainer);
				}
			}
			return *this;
		}


		void Init(CountType aNrOfRecomendedItems,CountType aItemIncreaseSize,bool aUseSafeModeFlag=true)
		{
			DL_CHK_ASSERT(myContainer == 0, "Array was already initialized");
			//DL_CHK_ASSERT(aNrOfRecomendedItems>0, "Initial size of array must be greater than 0");
			mySize = 0;
			myRealSize = aNrOfRecomendedItems;
			myGrowthFactor = aItemIncreaseSize;
			myUseSafeModeFlag = aUseSafeModeFlag;
			myContainer = new Type[myRealSize];
			//memset(myContainer, 0, myRealSize);
		}
		void ReInit(CountType aNrOfRecomendedItems,int aItemIncreaseSize,bool aUseSafeModeFlag=true)
		{
			DL_CHK_ASSERT(myContainer != 0, "Use of an undefined array");
			//DL_CHK_ASSERT(aNrOfRecomendedItems>0, "Initial size of array must be greater than 0");
			delete [] myContainer;
			mySize = 0;
			myRealSize = aNrOfRecomendedItems;
			myGrowthFactor = aItemIncreaseSize;
			myUseSafeModeFlag = aUseSafeModeFlag;
			myContainer = new Type[myRealSize];
			//memset(myContainer, 0, myRealSize);
		}

		inline const Type& operator[](const unsigned int& aIndex) const
		{
			DL_CHK_ASSERT(myContainer != 0, "Use of an undefined array");
			DL_CHK_ASSERT((aIndex<mySize), "Out of bounds error");
			return myContainer[aIndex];
		}

		inline Type& operator[](const unsigned int& aIndex)
		{
			DL_CHK_ASSERT(myContainer != 0, "Use of an undefined array");
			DL_CHK_ASSERT((aIndex<mySize), "Out of bounds error");
			return myContainer[aIndex];
		}

		inline void Add(const Type& aObject)
		{
			DL_CHK_ASSERT(myContainer != 0, "Use of an undefined array");
			if(mySize == myRealSize)
			{
				ReSize(myRealSize + myGrowthFactor);
			}
			myContainer[mySize++] = aObject;
		}
		inline void Insert(CountType aIndex, const Type& aObject)
		{
			DL_CHK_ASSERT(myContainer != 0, "Use of an undefined array");
			DL_CHK_ASSERT(aIndex>=0, "Out of bounds Error");
			if(mySize == myRealSize)
			{
				ReSize(myRealSize + myGrowthFactor);
			}
			CountType tmpSize = mySize - aIndex;
			mySize++;
			//Type *tmpContainer = new Type[tmpSize];
			if(myUseSafeModeFlag==true)
			{
				for(CountType index=mySize-1; index>aIndex; index--)
				{
					myContainer[index] = myContainer[index-1];
				}
			}
			else
			{
				//memcpy(tmpContainer, myContainer+aIndex, tmpSize * sizeof(Type));
				//memcpy(myContainer+aIndex+1, tmpContainer, tmpSize * sizeof(Type));
				memmove(myContainer+aIndex+1, myContainer+aIndex, tmpSize * sizeof(Type));
			}
			//delete [] tmpContainer;
			myContainer[aIndex] = aObject;
		}
		inline void DeleteCyclic(Type& aObject)
		{
			DL_CHK_ASSERT(myContainer != 0, "Use of an undefined array");
			for(CountType index=0; index<mySize; index++)
			{
				if(*myContainer[index] == *aObject)
				{
					DeleteCyclicAtIndex(index);
					break;
				}
			}
		}
		inline void DeleteCyclicAtIndex(unsigned int aItemNumber)
		{
			DL_CHK_ASSERT(myContainer != 0, "Use of an undefined array");
			DL_CHK_ASSERT((aItemNumber<mySize), "Out of bounds error");
			delete (myContainer[aItemNumber]);
			myContainer[aItemNumber] = 0;
			RemoveCyclicAtIndex(aItemNumber);
		}
		inline void PtrDeleteCyclic(Type& aObject)
		{
			DL_CHK_ASSERT(myContainer != 0, "Use of an undefined array");
			for(CountType index=0; index<mySize; index++)
			{
				if(*myContainer[index] == *aObject)
				{
					PtrDeleteCyclicAtIndex(index);
					break;
				}
			}
		}
		inline void PtrDeleteCyclicAtIndex(unsigned int aItemNumber)
		{
			DL_CHK_ASSERT(myContainer != 0, "Use of an undefined array");
			DL_CHK_ASSERT((aItemNumber<mySize), "Out of bounds error");
			//delete (myContainer[aItemNumber]);
			//myContainer[aItemNumber] = 0;
			RemovePtrCyclicAtIndex(aItemNumber);
		}
		inline void RemovePtrCyclicAtIndex(unsigned int aItemNumber)
		{
			DL_CHK_ASSERT(myContainer != 0, "Use of an undefined array");
			DL_CHK_ASSERT((aItemNumber<mySize), "Out of bounds error");
			myContainer[aItemNumber] = myContainer[mySize-1];
			myContainer[--mySize] = Type();
			//myContainer[mySize] = 0;
		}
		inline void RemoveCyclic(const Type& aObject)
		{
			DL_CHK_ASSERT(myContainer != 0, "Use of an undefined array");
			for(CountType index=0; index<mySize; index++)
			{
				if(myContainer[index] == aObject)
				{
					RemoveCyclicAtIndex(index);
					break;
				}
			}
		}
		inline void RemoveCyclicAtIndex(unsigned int aItemNumber)
		{
			DL_CHK_ASSERT(myContainer != 0, "Use of an undefined array");
			DL_CHK_ASSERT((aItemNumber<mySize), "Out of bounds error");
			myContainer[aItemNumber] = myContainer[--mySize];
			//myContainer[mySize] = 0;
		}
		inline void RemoveCyclicAtIndexBoost(unsigned int aItemNumber)
		{
			DL_CHK_ASSERT(myContainer != 0, "Use of an undefined array");
			DL_CHK_ASSERT((aItemNumber<mySize), "Out of bounds error");

			int lastIndex = Count() - 1;
			Type last =	myContainer[lastIndex];
			myContainer[lastIndex].reset();
			RemoveCyclicAtIndex(lastIndex);

			if( aItemNumber < Count() )
			{
				myContainer[aItemNumber] = last;
			}
		}
		inline void RemoveAtIndex(unsigned int aItemNumber)
		{
			DL_CHK_ASSERT(myContainer != 0, "Use of an undefined array");
			DL_CHK_ASSERT((aItemNumber<mySize), "Out of bounds error");

			mySize--;
			for(unsigned int index = aItemNumber; index < mySize; index++)
			{
				myContainer[index] = myContainer[index+1];
			}
		}
		inline void Remove(const Type& aObject)
		{
			DL_CHK_ASSERT(myContainer != 0, "Use of an undefined array");
			for(CountType index=0; index<mySize; index++)
			{
				if(myContainer[index] == aObject)
				{
					RemoveAtIndex(index);
					break;
				}
			}
		}


		inline void RemoveAll()
		{
			DL_CHK_ASSERT(myContainer != 0, "Use of an undefined array");
			mySize = 0;
		}

		inline void PtrDeleteAll()
		{
			DL_CHK_ASSERT(myContainer != 0, "Use of an undefined array");

			for(CountType index=0; index<mySize; index++)
			{
				myContainer[index] = Type();
			}

			mySize = 0;
		}

		inline void RemoveAllBoost()
		{
			DL_CHK_ASSERT(myContainer != 0, "Use of an undefined array");

			for(CountType index=0; index<mySize; index++)
			{
				myContainer[index].reset();
			}

			mySize = 0;
		}

		inline void DeleteAll()
		{
			DL_CHK_ASSERT(myContainer != 0, "Use of an undefined array");
			for(CountType index=0; index<mySize; index++)
			{
				delete (myContainer[index]);
				myContainer[index] = 0;
			}
			RemoveAll();
		}

		inline CountType Find(const Type &aValue)
		{
			DL_CHK_ASSERT(myContainer != 0, "Use of an undefined array");
			for(CountType index=0; index<mySize; index++)
			{
				if (myContainer[index] == aValue)
				{
					return index;
				}
			}
			return static_cast<CountType>(-1);
		}

		void Optimize()
		{
			DL_CHK_ASSERT(myContainer != 0, "Use of an undefined array");
			ReSize(mySize);
			myGrowthFactor = 1;
		}
		__forceinline unsigned int Count() const
		{
			DL_CHK_ASSERT(myContainer != 0, "Use of an undefined array");
			return static_cast<int>(mySize);
		}

	private:

		inline void ReSize(CountType aNewSize)
		{
			DL_CHK_ASSERT(myContainer != 0, "Use of an undefined array");
			myRealSize = aNewSize;
			Type *tmpContainer = myContainer;
			myContainer = new Type[myRealSize];
			Copy(tmpContainer);
			delete [] tmpContainer;
		}

		inline void Copy(const Type *aContainer)
		{
			DL_CHK_ASSERT(myContainer!=0&&aContainer!=0, "Use of an undefined array");
			if(myUseSafeModeFlag == false)
			{
				memcpy(myContainer, aContainer, mySize * sizeof(Type));
			}
			else
			{
				for(CountType index=0; index<mySize; index++)
				{
					myContainer[index] = aContainer[index];
				}
			}
		}
	};
};
namespace Utils=Utilities;

#endif
